﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://www.codeplex.com/EmlenMud
using System.Runtime.Serialization;
using System;
using System.Threading;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Collections;
using System.Text;
using BrainTechLLC.ThreadSafeObjects;
using System.ComponentModel;
using System.IO;
using Mud.EmlenMudMain;
using System.Diagnostics;

namespace BrainTechLLC
{
    [FlagsAttribute]
    [DataContract]
    public enum AreaFlags
    {
        [EnumMember]
        NONE = 0,
        [EnumMember]
        Modified = 1,
        [EnumMember]
        MarkedForDeletion = 2
    }

    [Serializable]
    [DataContract]
    public class AreaData : GameThingBaseClass
    {
        public Area ToArea()
        {
            Area result = new Area()
            {
                AreaFlags = areaflgs,
                OriginalFileName = origFileName,
                FileName = areaFName,
                VnumHigh = this.uvnum,
                VnumLow = this.lvnum,
                Name = new NameAndDescription()
                {
                    FriendlyUniqueName = this.Name.FriendlyUniqueName,
                    Keywords = this.Name.Name,
                    Description = this.desc,
                    ShortDescription = this.Name.Name,
                    OneLineDescription = this.areaFName
                }
            };

            if (clanzone > 0)
                result.AddOrSetProperty("ClanZone", this.clanzone);

            result.AddOrSetProperty("CanQuit", this.fcanquit);
            result.AddOrSetProperty("DescriptedRooms", this.ndescrooms);
            result.AddOrSetProperty("Jail", this.jail);
            result.AddOrSetProperty("OutJail", this.outjail);
            result.AddOrSetProperty("Open", this.open);
            result.AddOrSetProperty("MoreDry", this.more_dry);
            result.AddOrSetProperty("MoreRainy", this);
            result.AddOrSetProperty("MoreFog", this);
            result.AddOrSetProperty("NumberOfRooms", this.numofRooms);
            result.AddOrSetProperty("RepopMessage", this.repop);
            result.AddOrSetProperty("RepopCounter", this.repop_counter);
            result.AddOrSetProperty("RepopRate", this.repop_rate);
            result.AddOrSetProperty("Security", this.security);
            result.AddOrSetProperty("WeatherChange", this.weatherchg);
            result.AddOrSetProperty("Popped", this.areapopthisrboot);

            if (builders != null)
            {
                foreach (string s in builders)
                {
                    result.Builders.Add(s);
                }
            }

            result.Register();
            return result;
        }

        public string origFileName = "";
        public AreaFlags areaflgs = AreaFlags.NONE;		/* Area flags, changed, etc */
        public int security = 1;		/* Security, unused */
        public int numofRooms = 0;		/* Rooms in area */
        public List<string> builders = new List<string>();	/* Builder list */
        public string areaFName = "";
        public int jail = 0;			/* RoomVnumForJail vnum (unused) */
        public string desc = "";
        public int outjail = 0;		/* Out RoomVnumForJail vnum (unused) */
        public int fcanquit = 0;		/* PreventPlayersQuittingInArea status; quit rules */
        public int more_rainy = 0;		/* Not implimented */
        public int more_dry = 0;		/* Not implimented */
        public int open = 0;			/* OpenToPlayers to mortals? */
        public int more_fog = 0;		/* Not implimented */
        public int ndescrooms = 0;		/* Descripted currentRoom count */
        public int areapopthisrboot = 0; /* Has repop'd this reboot */
        public int lvnum = 0;			/* Lower vnum */
        public int uvnum = 0;			/* Upper vnum */
        public int nplayer = 0;		/* players in area */
        public int mob_prototype_count = 0;		/* Mobs in area */
        public int obj_prototype_count = 0;		/* Objs in area */
        public string repop = "";		/* RepopMessage message */
        public int repop_rate = 55;		/* RepopMessage rate in hours */
        public int clanzone = 0;		/* Is this a clan zone?  unused */
        public int weatherchg = 0;	/* Weather change.  unused */
        public int repop_counter = 0;		/* Current repop counter */
        /// <summary>
        /// These serialize out - containing shops, currentRoom repops, etc
        /// </summary> 
        public List<MobPrototypeData> rgMobPrototypes = new List<MobPrototypeData>(16);
        public List<ObjectPrototype> rgObjectPrototypes = new List<ObjectPrototype>(16);
        public List<RoomData> rgRooms = new List<RoomData>(16);
        public List<ResetData> rgResets = new List<ResetData>();	/* resets for this area */

#if NO_SILVERLIGHT
        [NonSerialized]
        public ReaderWriterLock syncRooms = new ReaderWriterLock();
#endif

        [Description("The area's original file name on disk"),
        Category("Naming and Reference")]
        public string OriginalFileName
        {
            get { return origFileName; }
            set { origFileName = value; }
        }

        [Category("Naming and Reference"), Description("The area's file name")]
        public string AreaFileName
        {
            get { return areaFName; }
            set { areaFName = value; }
        }

        [Category("Naming and Reference"), Description("A description of the area")]
        public string AreaDescription
        {
            get { return desc; }
            set { desc = value; }
        }

        [Category("Room VNUMs"), Description("The room vnum of the jail")]
        public int RoomVnumForJail
        {
            get { return jail; }
            set { jail = value; }
        }

        [Category("Room VNUMs"), Description("After serving a jail sentence, the player comes out of jail in this room vnum")]
        public int RoomVnumExitsJailTo
        {
            get { return outjail; }
            set { outjail = value; }
        }

        [Category("Area Behavior"), Description("Quit rules for the area (0=can quit, 1=CANNOT quit in this area)")]
        public int PreventPlayersQuittingInArea
        {
            get { return fcanquit; }
            set { fcanquit = value; }
        }

        [Category("Area Stats"), Description("Number of described rooms")]
        public int NumberOfDescribedRooms
        {
            get { return ndescrooms; }
            set { ndescrooms = value; }
        }

        [Category("Area Stats"), Description("How many times this area has pop'd since reboot")]
        public int TimesAreaHasPoppedSinceReboot
        {
            get { return areapopthisrboot; }
            set { areapopthisrboot = value; }
        }

        [Category("Area Behavior"), Description("If this area belongs to a clan, this stores the clan number")]
        public int Clanzone
        {
            get { return clanzone; }
            set { clanzone = value; }
        }

        [Category("Area Behavior"), Description("Weather change flags - currently unused")]
        public int WeatherChange
        {
            get { return weatherchg; }
            set { weatherchg = value; }
        }

        [Category("Area Properties"), Description("0 = no more rainy than usual. 1 = more rainy")]
        public int MoreRainy
        {
            get { return more_rainy; }
            set { more_rainy = value; }
        }

        [Category("Area Properties"), Description("0 = no more dry than usual. 1 = desert-like (?)")]
        public int MoreDry
        {
            get { return more_dry; }
            set { more_dry = value; }
        }

        [Category("Area Properties"), Description("0 = no more foggy than usual. 1 = foggy (does >1 do anything?)")]
        public int MoreFoggy
        {
            get { return more_fog; }
            set { more_fog = value; }
        }

        [Category("Area Properties"), Description("Is the area open to mortals?  0 = NO, 1 = yes.")]
        public int OpenToPlayers
        {
            get { return open; }
            set { open = value; }
        }

        [Category("Area Behavior"), Description("Area flags (indicating if area has changed, should be deleted, etc)")]
        //Editor(typeof(Utils.FlagEnumUIEditor), typeof(System.Drawing.Design.UITypeEditor))]
        public AreaFlags AreaFlagsSet
        {
            get { return areaflgs; }
            set { areaflgs = value; }
        }

        [Category("Area Properties"), Description("Area security.  Currently unused??")]
        public int Security
        {
            get { return security; }
            set { security = value; }
        }

        [Category("Naming and Reference"), Description("The names of the builders authorized to build in this area.")]
        public List<string> Builders
        {
            get { return builders; }
            set { builders = value; }
        }

        [Category("Room VNUMs"), Description("The lower bound (minimum VNUM) in this area for rooms, mob prototypes, and object prototypes")]
        public int LowestVnumBelongingToArea
        {
            get { return lvnum; }
            set { lvnum = value; }
        }

        [Category("Room VNUMs"), Description("The upper bound (maximum VNUM) in this area for rooms, mob prototypes, and object prototypes")]
        public int HighestVnumBelongingToArea
        {
            get { return uvnum; }
            set { uvnum = value; }
        }

        [Category("Area Properties"), Description("Number of players currently in the area")]
        public int PlayersInArea
        {
            get { return nplayer; }
            set { nplayer = value; }
        }

        [Description("The total number of rooms in this area"),
        Category("Area Properties")]
        public int NumberOfRoomsInArea
        {
            get { return numofRooms; }
            set { numofRooms = value; }
        }

        [Description("The total number of mob prototypes in this area"),
        Category("Area Properties")]
        public int TotalMobPrototypes
        {
            get { return mob_prototype_count; }
            set { mob_prototype_count = value; }
        }

        [Description("The total number of object prototypes in this area"),
        Category("Area Properties")]
        public int TotalObjectPrototypes
        {
            get { return obj_prototype_count; }
            set { obj_prototype_count = value; }
        }

        [Description("A message that is displayed when the area repops"),
        Category("Area Behavior")]
        public string RepopMessage
        {
            get { return repop; }
            set { repop = value; }
        }

        [Description("Repop rate (unsure of the timing on this.. says HOURS?!)"),
        Category("Area Behavior")]
        public int RepopRateInMinutes
        {
            get { return repop_rate; }
            set { repop_rate = value; }
        }

        [Description("The countdown until repop occurs for this area"),
        Category("Area Properties")]
        public int RepopCountdownMinutes
        {
            get { return repop_counter; }
            set { repop_counter = value; }
        }

        [Description("The actual mob prototypes in this area"),
        Category("Mobs, Objects, Rooms, etc")]//, TypeConverter(typeof(MobPrototype))]
        public List<MobPrototypeData> MobPrototypesBelongingToArea
        {
            get { return rgMobPrototypes; }
            set { rgMobPrototypes = value; }
        }

        [Description("The actual object prototypes in this area"),
        Category("Mobs, Objects, Rooms, etc")]//, TypeConverter(typeof(ObjectPrototype))]
        public List<ObjectPrototype> ObjectPrototypesBelongingToArea
        {
            get { return rgObjectPrototypes; }
            set { rgObjectPrototypes = value; }
        }

        [Description("The actual rooms in this area"),
        Category("Mobs, Objects, Rooms, etc")]//, TypeConverter(typeof(RoomData))]
        public List<RoomData> RoomsInArea
        {
            get { return rgRooms; }
            set { rgRooms = value; }
        }

        [Description("All of the resets in this area"),
        Category("Mobs, Objects, Rooms, etc")]//, TypeConverter(typeof(ResetData))]
        public List<ResetData> ResetsInArea
        {
            get { return rgResets; }
            set { rgResets = value; }
        }

        public int GetNextFreeRoomVnum()
        {
            int nMaxVnum = lvnum - 1;

            for (int n = 0; n < rgRooms.Count; n++)
            {
                RoomData room = (RoomData)rgRooms[n];
                if (room.VnumRoom > nMaxVnum)
                {
                    nMaxVnum = room.VnumRoom;
                }
            }
            if (nMaxVnum == -1) nMaxVnum = 0;
            if (nMaxVnum >= uvnum) return -1;
            return nMaxVnum + 1;
        }

        public int AddNewRoom()
        {
            //if (EmlenMud == null) AssignEmlenMud(EmlenMud);
            RoomData room = new RoomData();
            room.Name = new NameAndDescription() { Name = "Room Title" };
            room.RoomDescription = "This is the room description...";
            return AddNewRoom(room);
        }

        public int AddNewRoom(RoomData room)
        {
            //if (syncRooms == null) syncRooms = new ReaderWriterLock();

            //syncRooms.AcquireWriterLock(5000);
            //if (room.EmlenMud == null) ((RoomData)room).AssignEmlenMud(EmlenMud);
            int newvnum = (room.VnumRoom <= 0) ? GetNextFreeRoomVnum() : room.VnumRoom;
            if (newvnum == -1) return -1; // no MoreRoomInfo free vnums in area
            if (newvnum == 0) newvnum = 1;
            room.VnumRoom = newvnum;
            room.area = this;
            rgRooms.Add(room);
            numofRooms++;
            EmlenMud.WorldTemplates.htRooms.Add(newvnum, room);
            //syncRooms.ReleaseWriterLock();

            if (newvnum > EmlenMud.WorldTemplates.top_room_vnum) EmlenMud.WorldTemplates.top_room_vnum = newvnum;
            if (open == 1)
            {
                EmlenMud.WorldTemplates.nRoomsOpen++;
            }
            return newvnum;
        }

        public void load_old_area_format(StreamReader sr, ref AreaData pArea)
        {
            char[] spc = " ".ToCharArray();
            char[] tilde = "~".ToCharArray();

            while (sr.EndOfStream == false)
            {
                string szLine = sr.ReadLine().Trim();
                if (szLine == "End")
                {
                    break;
                }
                string[] rgsz = szLine.Split(spc, 2, StringSplitOptions.RemoveEmptyEntries);
                string szWord = rgsz[0];
                if (szWord == "Name")
                {
                    pArea.Name = new NameAndDescription() { Name = rgsz[1].TrimEnd(tilde), FriendlyUniqueName = rgsz[1].TrimEnd(tilde) };
                    continue;
                }
                if (szWord == "NQuit")
                {
                    pArea.PreventPlayersQuittingInArea = Convert.ToInt32(rgsz[1]);
                    continue;
                }
                if (szWord == "Security")
                {
                    pArea.Security = Convert.ToInt32(rgsz[1]);
                    continue;
                }
                if (szWord == "SRepop")
                {
                    pArea.RepopRateInMinutes = Convert.ToInt32(rgsz[1]);
                    continue;
                }

                if (szWord == "SJail")
                {
                    pArea.RoomVnumForJail = Convert.ToInt32(rgsz[1]);
                    continue;
                }
                if (szWord == "SCZone") { pArea.Clanzone = Convert.ToInt32(rgsz[1]); continue; }
                if (szWord == "SOutJail") { pArea.RoomVnumExitsJailTo = Convert.ToInt32(rgsz[1]); continue; }
                if (szWord == "VNUMs")
                {
                    string[] rgSpl2 = rgsz[1].Split(spc, 2, StringSplitOptions.RemoveEmptyEntries);
                    pArea.LowestVnumBelongingToArea = Convert.ToInt32(rgSpl2[0]);
                    pArea.HighestVnumBelongingToArea = Convert.ToInt32(rgSpl2[1]);
                    continue;
                }

                if (szWord == "Builders")
                {
                    string[] rgSpl3 = rgsz[1].Split(spc, StringSplitOptions.RemoveEmptyEntries);
                    for (int j = 0; j < rgSpl3.Length; j++)
                    {
                        pArea.Builders.Add(rgSpl3[j].TrimEnd('~'));
                    }
                    continue;
                }
                if (szWord == "Open") { pArea.OpenToPlayers = Convert.ToInt32(rgsz[1]); continue; }
                if (szWord == "Rcount") { pArea.NumberOfRoomsInArea = Convert.ToInt32(rgsz[1]); continue; }
                if (szWord == "Repop") { pArea.RepopMessage = rgsz[1].TrimEnd('~'); continue; }
                if (szWord == "Reset") { continue; }  // ?????
            }
            pArea.RepopCountdownMinutes = 1; // TODO EmlenMud.RandIntBetween(1, pArea.RepopRateInMinutes);
        }

        public void save_old_area_format(System.IO.StreamWriter sw, AreaData pArea)
        {
            sw.WriteLine("Name " + pArea.Name + "~");
            sw.WriteLine("NQuit " + pArea.PreventPlayersQuittingInArea.ToString());
            sw.WriteLine("Security " + pArea.Security.ToString());
            sw.WriteLine("SRepop " + pArea.RepopRateInMinutes.ToString());
            sw.WriteLine("SJail " + pArea.RoomVnumForJail.ToString());
            sw.WriteLine("SCZone " + pArea.Clanzone.ToString());
            sw.WriteLine("SOutJail " + pArea.RoomVnumExitsJailTo.ToString());
            sw.WriteLine("VNUMs " + pArea.LowestVnumBelongingToArea.ToString() + " " + pArea.HighestVnumBelongingToArea.ToString());
            sw.Write("Builders");
            for (int n = 0; n < pArea.Builders.Count; n++)
            {
                string sz = (string)pArea.Builders[n];
                sw.Write(" " + sz);
            }
            sw.WriteLine("~");
            sw.WriteLine("Open " + pArea.OpenToPlayers.ToString());
            sw.WriteLine("Rcount " + pArea.NumberOfRoomsInArea.ToString());
            sw.WriteLine("Repop " + pArea.RepopMessage + "~");
            // sw.WriteLine("Reset " + ?????);
            sw.WriteLine("End");
            sw.WriteLine("");
        }


        public AreaData LoadSingleArea(string szFullPathAndFileName, OldSchoolRooms osRooms,
          OldSchoolMobPrototyes osMP, OldSchoolObjPrototypes osOP)
        {
            FileStream fsArea = null;
            try
            {
                fsArea = new FileStream(szFullPathAndFileName, FileMode.Open, FileAccess.Read);
            }
            catch (IOException ex)
            {
                Debug.WriteLine("LoadSingleArea: " + ex.Message);
                return null;
            }
            StreamReader srArea = new StreamReader(fsArea);
            AreaData area = (AreaData)LoadSingleArea(srArea, osRooms, osMP, osOP);
            srArea.Close();
            fsArea.Close();
            return area;
        }

        public AreaData LoadSingleArea(StreamReader srArea, OldSchoolRooms osRooms,
          OldSchoolMobPrototyes osMP, OldSchoolObjPrototypes osOP)
        {
            return LoadSingleArea(srArea, "", osRooms, osMP, osOP);
        }

        public AreaData LoadSingleArea(StreamReader srArea, string szArea, OldSchoolRooms osRooms,
          OldSchoolMobPrototyes osMP, OldSchoolObjPrototypes osOP)
        {
            AreaData area = null;
            while (srArea.EndOfStream == false)
            {
                char c = ' ';
                while (c != '#' && srArea.EndOfStream == false) c = (char)srArea.Read();
                if (c != '#')
                {
#if NO_SILVERLIGHT
                    GlobalLogger.Log("Error", "Area " + szArea + " possibly corrupt?  No #...", LogSeverity.Critical);
#endif

                    break;
                }
                string szSection = srArea.ReadLine().Trim();

                if (area == null)
                {
                    area = new AreaData();
                    area.Name = new NameAndDescription() { Name = szArea, FriendlyUniqueName = szArea };
                }

                if (szSection[0] == '$')
                {
                    //if (EmlenMud != null) EmlenMud.OutputMessage("Area " + szArea + " loaded successfully!");
                    area.AreaFileName = szArea;
                    //if (EmlenMud != null)
                    {
                        area.RepopCountdownMinutes = 1; // TODO HEREHERE area.RandIntBetween(1, area.RepopRateInMinutes);
                        //EmlenMud.WorldTemplates.AllAreas.Add(area);
                        //EmlenMud.WorldTemplates.nAreaCount++;
                        //if (area.OpenToPlayers != 0) EmlenMud.WorldTemplates.open_areas++;
                        //EmlenMud.WorldTemplates.AllAreas.Sort();
                    }
                    break;
                }
                else if (szSection == "HELPS")
                {
                    //if (EmlenMud == null) return null;
                    HelpData.load_old_style_helps(srArea);
                }
                else if (szSection == "AREADATA")
                {
                    AreaData areaI = (AreaData)area;
                    load_old_area_format(srArea, ref areaI);
                }
                else if (szSection == "MOBDATA")
                {
                    osMP.load_mobiles_old_format(srArea, area);
                }
                else if (szSection == "OBJDATA")
                {
                    osOP.load_objects_old_format(srArea, area);
                }
                else if (szSection == "ROOMDATA")
                {
                    osRooms.load_rooms_old_format(srArea, area);
                }
                else
                {
#if NO_SILVERLIGHT
                    GlobalLogger.Log("Error", "Area " + szArea + " had bad section name: " + szSection, LogSeverity.Error);
#endif
                }
            }
            return area;
        }

        // TODO        
        public AreaList _areaList = new AreaList();

        public void LoadOldFormatAreasMobsObjectsEtc(string szAreaList)
        {
            for (int i = 0; i < _areaList.AreaFileNames.Count; i++)
            {
                string szArea = (string)_areaList.AreaFileNames[i];
                if (szArea == "$") break;
                FileStream fsArea = null;
                string szFullPathAndFileName = szAreaList + szArea;
                try
                {
                    fsArea = new FileStream(szFullPathAndFileName, FileMode.Open, FileAccess.Read);
                }
                catch (IOException ex)
                {
                    EmlenMud.LogError("Error finding area file " + szFullPathAndFileName + " : ", ex, LogType.Always);
                    continue;
                }

                StreamReader srArea = new StreamReader(fsArea);
                AreaData area = (AreaData)LoadSingleArea(srArea, szArea, EmlenMud.OldschoolRoom,
                  EmlenMud.OldschoolMP, EmlenMud.OldschoolOP);
                srArea.Close();
                fsArea.Close();
                if (area != null) area.OriginalFileName = szFullPathAndFileName;
            }
        }

        public void SaveSingleArea(StreamWriter sw, AreaData area,
          OldSchoolRooms osRooms, OldSchoolMobPrototyes osMP, OldSchoolObjPrototypes osOP)
        {
            sw.WriteLine("#AREADATA");
            save_old_area_format(sw, area);
            sw.WriteLine("");

            sw.WriteLine("#MOBDATA");
            for (int n = 0; n < area.MobPrototypesBelongingToArea.Count; n++)
            {
                MobPrototype mp = (MobPrototype)area.MobPrototypesBelongingToArea[n];
                osMP.WriteOldMobileFormat(sw, mp.Vnum, mp, area);
            }
            sw.WriteLine("#0");
            sw.WriteLine("");

            sw.WriteLine("#OBJDATA");
            for (int n = 0; n < area.ObjectPrototypesBelongingToArea.Count; n++)
            {
                ObjectPrototype op = (ObjectPrototype)area.ObjectPrototypesBelongingToArea[n];
                osOP.WriteOldObjectFormat(sw, op.Vnum, op, area);
            }
            sw.WriteLine("#0");
            sw.WriteLine("");

            sw.WriteLine("#ROOMDATA");
            for (int n = 0; n < area.RoomsInArea.Count; n++)
            {
                RoomData room = (RoomData)area.RoomsInArea[n];
                osRooms.WriteOldRoomFormat(sw, room.VnumRoom, room, area);
            }
            sw.WriteLine("#0");
            sw.WriteLine("");
            sw.WriteLine("#$");
        }

        public void SaveOldFormatAreasMobsObjectsEtc(string szAreaList)
        {
            for (int i = 0; i < EmlenMud.WorldTemplates.arealist.AreaFileNames.Count; i++)
            {
                string szArea = (string)EmlenMud.WorldTemplates.arealist.AreaFileNames[i];
                if (szArea == "$") break;
                AreaData area = EmlenMud.WorldTemplates.FindArea(szArea);
                if (area == null)
                {
                    EmlenMud.LogError("Could not find area " + szArea + " in memory!", null, LogType.Always | LogType.WorldError);
                    continue;
                }
                FileStream fsArea = null;
                try
                {
                    fsArea = new FileStream(szAreaList + szArea, FileMode.Create, FileAccess.Write);
                }
                catch (IOException ex)
                {
                    EmlenMud.LogError("Error creating area " + szAreaList + szArea + " : ", ex, LogType.Always);
                    continue;
                }

                StreamWriter sw = new StreamWriter(fsArea);
                SaveSingleArea(sw, area, EmlenMud.OldschoolRoom,
                  EmlenMud.OldschoolMP, EmlenMud.OldschoolOP);
                sw.Flush();
                sw.Close();
                fsArea.Close();
            }
        }
    }


}
